package com.reus.execute;

import com.reus.cache.Cache;
import com.reus.cache.LruCache;
import com.reus.cache.SimpleCache;
import com.reus.core.Config;
import com.reus.core.MapperCore;
import com.reus.datasource.PoolDataSource;
import com.reus.statement.PreparedStatementHandle;
import com.reus.statement.ResultSetHandle;
import com.reus.transaction.Transaction;
import com.reus.transaction.TransactionFactory;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * description: 简单执行器类，对于Sql的执行和处理都依赖此类
 * copyright: Copyright (c) 2018-2021
 * company: iSysCore Tech. Co., Ltd.
 *
 * @author liuxq@isyscore.com
 * @version 1.0
 * @date 2021-10-19 19:16:42
 */
public class SimpleExecutor implements Executor {

    /**
     * 全局配置类
     */
    public Config config;

    /**
     * mapper核心类，解析mapper接口类
     */
    public MapperCore mapperCore;

    /**
     * 数据库源
     */
    public PoolDataSource poolDataSource;

    /**
     * 事务
     */
    public Transaction transaction;

    /**
     * 缓存
     */
    public Cache cache;

    public SimpleExecutor(Config config, PoolDataSource poolDataSource) {
        this(config, poolDataSource, false, false);
    }

    public SimpleExecutor(Config config, PoolDataSource poolDataSource, boolean openTransaction, boolean openCache) {
        this.config = config;
        this.mapperCore = config.getMapperCore();
        this.poolDataSource = poolDataSource;
        if (openCache) {
            // 设置缓存策略为LRU
            this.cache = new LruCache(new SimpleCache());
        }
        if (openTransaction) {
            // 关闭自动提交
            this.transaction = TransactionFactory.newTransaction(poolDataSource, Connection.TRANSACTION_REPEATABLE_READ, false);
        } else {
            // 开启自动提交
            this.transaction = TransactionFactory.newTransaction(poolDataSource, null, null);
        }
    }

    /**
     * @Desc 获取mapper接口的代理类
     * 为什么要使用代理类，因为我们写mapper接口类只写了接口，却没有提供它的实现。于是
     * 使用动态代理机制对这些接口进行代理，在代理类中实现sql执行的方法。此处是参照
     * mybatis的设计。
     **/
    @Override
    public <T> T getMapper(Class<T> type) {
        /**MapperProxy代理类分析见详情8.1*/
        MapperProxy mapperProxy = new MapperProxy(type, this);
        return (T) mapperProxy.initialization();
    }

    /*select 语句执行流程分析**/
    public <E> List<E> select(Method method, Object[] args) throws Exception {

        /**PreparedStatementHandle 生成器见9.1 */
        PreparedStatementHandle preparedStatementHandle = new PreparedStatementHandle(mapperCore, transaction, method, args);
        PreparedStatement preparedStatement = preparedStatementHandle.generateStatement();

        /**执行查询接口，此处是真实调用sql语句的地方 */
        preparedStatement.executeQuery();
        ResultSet resultSet = preparedStatement.getResultSet();

        /**查询方法的返回参数值，若是void类型，就不用返回任务东西 */
        Class returnClass = mapperCore.getReturnType(method);
        if (returnClass == null || void.class.equals(returnClass)) {
            return null;
        } else {
            /**ResultSetHandle 结果处理器见9.2 */
            ResultSetHandle resultSetHandle = new ResultSetHandle(returnClass, resultSet);
            return resultSetHandle.handle();
        }
    }

    /*update 语句执行流程分析,update，delete,insert都是调用的这个方法**/
    public int update(Method method, Object[] args) throws SQLException {
        PreparedStatementHandle preparedStatementHandle = null;
        PreparedStatement preparedStatement = null;
        Integer count = null;
        try {
            /**PreparedStatementHandle 生成器见9.1 */
            preparedStatementHandle = new PreparedStatementHandle(mapperCore, transaction, method, args);
            preparedStatement = preparedStatementHandle.generateStatement();

            /**返回受影响的行数 */
            count = preparedStatement.executeUpdate();
        } finally {
            if (preparedStatement != null) {
                preparedStatement.close();
            }
        }
        return count;
    }

    /**
     * 后续方法直接调用transaction相关方法
     */
    @Override
    public void commit() throws SQLException {
        transaction.commit();
    }

    @Override
    public void rollback() throws SQLException {
        transaction.rollback();
    }

    @Override
    public void close() throws SQLException {
        transaction.close();
    }
}